// Package system
// @Author zhongxc
// @Date 2024/7/9 17:38:00
// @Desc
package system

import (
	"context"
	"fmt"
	"google.golang.org/protobuf/types/known/emptypb"
	"net/http"
	"strconv"
	"strings"
	"time"
	"vben-api/api/rpc"
	"vben-api/pkg/svc"
	common "vben-common"
	"vben-common/errs"
	"vben-common/utils"
	typeMenu "vben-grpc-common/vben-admin/client/types/menu"
	menuService "vben-grpc-common/vben-admin/types/menu.service.v1"

	"github.com/gin-gonic/gin"
	"github.com/google/martian/log"
	"github.com/jinzhu/copier"
)

type Menu struct {
	svcCtx *svc.ServiceContext
}

func NewMenu(svcCtx *svc.ServiceContext) *Menu {
	return &Menu{
		svcCtx: svcCtx,
	}
}

// GetMenuInfo
// @Description: 获取菜单详情
// @receiver m
// @param c
func (m *Menu) GetMenuInfo(c *gin.Context) {
	result := common.Result{}
	idStr := c.Query("id")
	id, _ := strconv.ParseInt(idStr, 10, 64)
	res, err := rpc.MenuServiceClient.GetMenuById(context.Background(), &menuService.GetMenuRequest{Id: id})
	if err != nil {
		code, msg := errs.ParseGrpcError(err)
		c.JSON(http.StatusBadRequest, result.Fail(code, msg))
		return
	}
	var menu typeMenu.ClientMenu
	_ = copier.Copy(&menu, &res)

	c.JSON(http.StatusOK, result.Success(menu))
}

// GetMenuList
// @Description: 菜单列表
// @receiver m
// @param ctx
func (m *Menu) GetMenuList(ctx *gin.Context) {
	result := &common.Result{}
	name := ctx.Query("name")
	// 如果status为空，则默认为 1
	strStatus := ctx.DefaultQuery("status", "1")

	log.Infof("name: %s, status: %s", name, strStatus)
	intStatus, _ := strconv.ParseInt(strStatus, 10, 32)

	res, err := rpc.MenuServiceClient.GetMenuList(context.Background(), &menuService.MenuListRequest{
		Name:   name,
		Status: int32(intStatus),
	})

	if err != nil {
		code, msg := errs.ParseGrpcError(err)
		ctx.JSON(http.StatusBadRequest, result.Fail(code, msg))
		return
	}

	ctx.JSON(http.StatusOK, result.Success(m.transformToMenuNode(res.Data, 0)))
}

func (m *Menu) transformToMenuNode(data []*menuService.MenuItem, parentId int64) []*ListNode {
	var nodes = make([]*ListNode, 0)
	if data == nil || len(data) == 0 {
		return nodes
	}
	for _, item := range data {
		if item.ParentId == parentId {
			listNode := &ListNode{
				Id:         item.Id,
				ParentId:   item.ParentId,
				ParentName: "",
				Name:       item.Name,
				Url:        item.Url,
				Perms:      item.Perms,
				Type:       item.Type,
				Icon:       item.Icon,
				OrderNo:    item.OrderNo,
				Status:     item.Status,
				CreatedBy:  item.CreatedBy,
				UpdatedBy:  item.UpdatedBy,
			}
			listNode.Children = m.buildMenuNodeChildren(item.Children, item.Id, item.Name)
			nodes = append(nodes, listNode)
		}
	}
	return nodes
}

func (m *Menu) buildMenuNodeChildren(data []*menuService.MenuItem, parentId int64, parentName string) []*ListNode {
	var children = make([]*ListNode, 0)
	if len(data) == 0 {
		return children
	}
	for _, item := range data {
		if item.ParentId == parentId {

			listNode := &ListNode{
				Id:         item.Id,
				ParentId:   item.ParentId,
				ParentName: parentName,
				Name:       item.Name,
				Url:        item.Url,
				Perms:      item.Perms,
				Type:       item.Type,
				Icon:       item.Icon,
				OrderNo:    item.OrderNo,
				Status:     item.Status,
				CreatedBy:  item.CreatedBy,
				CreatedAt:  item.CreatedAt,
				UpdatedBy:  item.UpdatedBy,
			}
			listNode.Children = m.buildMenuNodeChildren(item.Children, item.Id, item.Name)
			children = append(children, listNode)
		}
	}
	return children
}

type ListNode struct {
	Id         int64       `form:"id" json:"id"`
	ParentId   int64       `form:"parent_id" json:"parent_id"`
	ParentName string      `form:"parent_name" json:"parent_name"`
	Name       string      `form:"name" json:"name"`
	Url        string      `form:"url" json:"url"`
	Perms      string      `form:"perms" json:"perms"`
	Type       int32       `form:"type" json:"type"`
	Icon       string      `form:"icon" json:"icon"`
	OrderNo    int64       `form:"order_no" json:"order_no"`
	Status     int32       `form:"status" json:"status"`
	CreatedBy  string      `form:"created_by" json:"created_by"`
	CreatedAt  string      `form:"created_at" json:"created_at"`
	UpdatedBy  string      `form:"updated_by" json:"updated_by"`
	UpdatedAt  string      `form:"updated_at" json:"updated_at"`
	Children   []*ListNode `form:"children" json:"children,omitempty"`
}

// SaveMenu
// @Description: 创建菜单
// @receiver m
// @param ctx
func (m *Menu) SaveMenu(ctx *gin.Context) {
	result := &common.Result{}
	fmt.Printf("name=%s\n", ctx.PostForm("name"))
	var menuRequest menuService.CreateMenuRequest

	if err := ctx.ShouldBindJSON(&menuRequest); err != nil {
		ctx.JSON(http.StatusOK, result.Fail(400, "参数错误"))
		return
	}

	res, err := rpc.MenuServiceClient.CreateMenu(ctx, &menuRequest)
	if err != nil {
		code, msg := errs.ParseGrpcError(err)
		ctx.JSON(http.StatusOK, result.Fail(code, msg))
		return
	}

	ctx.JSON(http.StatusOK, result.Success(res))
}

// UpdateMenu
// @Description: 更新菜单
// @receiver m
// @param ctx
func (m *Menu) UpdateMenu(ctx *gin.Context) {
	result := common.NewResult()

	var menuRequest menuService.UpdateMenuRequest
	var updateMenu typeMenu.UpdateMenu

	if err := ctx.ShouldBind(&updateMenu); err != nil {
		ctx.JSON(http.StatusOK, result.Fail(400, "参数错误"))
		return
	}
	err := copier.Copy(&menuRequest, &updateMenu)
	if err != nil {
		ctx.JSON(http.StatusOK, result.Fail(400, "参数错误"))
		return
	}

	resp, err := rpc.MenuServiceClient.UpdateMenu(context.Background(), &menuRequest)
	if err != nil {
		code, msg := errs.ParseGrpcError(err)
		ctx.JSON(http.StatusOK, result.Fail(code, msg))
		return
	}

	ctx.JSON(http.StatusOK, result.Success(resp))
}

// DeleteMenu
// @Description: 删除菜单
// @receiver m
// @param c
func (m *Menu) DeleteMenu(c *gin.Context) {
	result := common.NewResult()
	ids := c.PostForm("ids")
	if len(ids) == 0 {
		c.JSON(http.StatusOK, result.Fail(400, "参数错误"))
		return
	}
	idSlice := strings.Split(ids, ",")
	idIntSlice, _ := utils.StringSliceToIntSlice(idSlice)
	resp, err := rpc.MenuServiceClient.DeleteMenu(context.Background(), &menuService.DeleteMenuRequest{Ids: idIntSlice})
	if err != nil {
		code, msg := errs.ParseGrpcError(err)
		c.JSON(http.StatusOK, result.Fail(code, msg))
		return
	}
	c.JSON(http.StatusOK, result.Success(resp))
}

func (m *Menu) SelectMenuTree(ctx *gin.Context) {
	result := common.NewResult()
	resp, err := rpc.MenuServiceClient.SelectMenuTree(context.Background(), &emptypb.Empty{})
	if err != nil {
		code, msg := errs.ParseGrpcError(err)
		ctx.JSON(http.StatusOK, result.Fail(code, msg))
		return
	}
	ctx.JSON(http.StatusOK, result.Success(resp.Data))
}

// GetNavList
// @Description: 获取导航栏菜单
// @receiver m
// @param c
func (m *Menu) GetNavList(c *gin.Context) {
	r := common.NewResult()
	ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancelFunc()
	in := &menuService.SelectMenuByUserIdRequest{
		UserId: m.svcCtx.GetUserInfo().UserId,
	}
	list, err := rpc.MenuServiceClient.SelectMenuByUserId(ctx, in)
	if err != nil {
		code, msg := errs.ParseGrpcError(err)
		c.JSON(http.StatusOK, r.Fail(code, msg))
		return
	}
	routeTree := m.buildRouteTree(list.Data, 0)
	c.JSON(http.StatusOK, r.Success(routeTree))
}

func (m *Menu) buildRouteTree(data []*menuService.MenuItem, parentId int64) []*RouteItem {
	var routeItemList = make([]*RouteItem, 0)
	if len(data) == 0 {
		return routeItemList
	}
	rootRouteItem := &RouteItem{
		Path:      "/dashboard",
		Name:      "Dashboard",
		Component: "LAYOUT",
		Redirect:  "/dashboard/analysis",
		Meta:      RouteMeta{Title: "routes.dashboard.dashboard", Icon: "ion:grid-outline", Affix: false},
	}
	rootRouteItem.Children = []*RouteItem{
		{
			Path:      "analysis",
			Name:      "Analysis",
			Component: "dashboard/analysis/index",
			Meta:      RouteMeta{Title: "routes.dashboard.dashboard", Affix: false},
		}, {
			Path:      "workbench",
			Name:      "Workbench",
			Component: "dashboard/workbench/index",
			Meta:      RouteMeta{Title: "routes.dashboard.workbench", Affix: false},
		},
	}
	routeItemList = append(routeItemList, rootRouteItem)
	for _, item := range data {
		if item.ParentId == parentId {
			routeItem := &RouteItem{
				Path:      "/" + item.Url,
				Name:      item.Name,
				Component: item.Component,
				Meta:      RouteMeta{Title: item.Name, Affix: false, Icon: item.Icon},
			}
			routeItem.Children = m.buildRouteTreeChild(item.Children, item.Id)
			routeItemList = append(routeItemList, routeItem)
		}
	}
	return routeItemList
}

func (m *Menu) buildRouteTreeChild(data []*menuService.MenuItem, parentId int64) []*RouteItem {
	var childRouteItem = make([]*RouteItem, 0)
	for _, item := range data {
		if item.ParentId == parentId {
			routeItem := &RouteItem{
				Path:          "child_" + strconv.FormatInt(item.Id, 10),
				Component:     item.Component,
				Meta:          RouteMeta{Title: item.Name, Affix: false, Icon: item.Icon},
				Name:          item.Name,
				CaseSensitive: false,
			}
			child := m.buildRouteTreeChild(item.Children, item.Id)
			routeItem.Children = child
			childRouteItem = append(childRouteItem, routeItem)
		}
	}
	return childRouteItem
}

type RouteItem struct {
	Path          string       `json:"path" form:"path"`
	Name          string       `json:"name" form:"name"`
	Component     string       `json:"component" form:"component"`
	Meta          RouteMeta    `json:"meta" form:"meta"`
	Alias         string       `json:"alias,omitempty" form:"alias"`
	Redirect      string       `json:"redirect,omitempty" form:"redirect"`
	CaseSensitive bool         `json:"caseSensitive,omitempty" form:"caseSensitive"`
	Children      []*RouteItem `json:"children,omitempty" form:"children" `
}

type RouteMeta struct {
	Title string `json:"title" form:"title"`
	Affix bool   `json:"affix,omitempty" form:"affix"`
	Icon  string `json:"icon" form:"icon"`
}
